#ifndef NIGHT_TENSOR_SHAPE
#define NIGHT_TENSOR_SHAPE

#include "core.hpp"

namespace night { 

namespace tensor {

template< int n, typename Shape > struct element;
template< typename Type, typename Shape > struct find;
template< typename Type, typename Shape > struct join;
template< typename Type, typename Shape > struct erase;

template< typename Type, typename Shape > inline Type & get_index ( Shape const & s ) { return find< Type, Shape >::apply (s); }

template< typename Type > inline shape< Type, shape<> > make_shape ( Type & x ) { return shape< Type, shape<> > (x); }

template<> struct shape<> 
{
	static int const size = 0;

	typedef void result_type;

	shape () {}

	shape ( shape const & ) {}

	template< typename Head, typename Tail >
	explicit shape ( shape<Head,Tail> const & ) {}

	template< typename S >
	shape< S, shape > operator << ( S & src ) const { return shape< S, shape > ( src ); }
};

template< typename T, typename Base >
struct shape : Base
{
	static int const size = Base::size+1;

	typedef Base base_type;
	typedef T type;
	typedef T & result_type;

	T & value;

	explicit shape ( T & src, base_type const & s = base_type () ) : base_type (s), value (src) {}
	template< typename Head, typename Tail >
	explicit shape ( shape<Head,Tail> const & s ) : base_type (s), value ( get_index<type> (s) ) {}

	template< typename S >
	shape< S, shape > operator << ( S & src ) const { return shape< S, shape > ( src, *this ); }

	Base const & next () const { return (*this); }
};



template< typename Type >
struct find< Type, shape<> > 
{
	static int const value = 0;
	typedef void type;
};

template< typename Type, typename Tail >
struct find< Type, shape<Type,Tail> > 
{
	static int const value = 1;
	typedef shape< Type, Tail > type;

	static Type & apply ( shape<Type,Tail> const & s ) { return s.value; }
};

template< typename Type, typename Head, typename Tail >
struct find< Type, shape<Head,Tail> > 
{
	static int const value = find< Type, Tail >::value;
	typedef typename find< Type, Tail >::type type;

	static Type & apply ( shape<Head,Tail> const & s ) { 
		return static_cast< type const & > (s).value;
	}
};


template< >
struct join< shape<>, shape<> > {
	typedef shape<> type;
};

template< typename Type >
struct join< shape<>, Type > {
	typedef shape< Type, shape<> > type;
};

template< typename Type >
struct join< Type, shape<> > {
	typedef shape< Type, shape<> > type;
};

template< typename Head, typename Tail >
struct join< shape<>, shape<Head,Tail> > {
	typedef shape<Head,Tail> type;
};

template< typename Head, typename Tail >
struct join< shape<Head,Tail>, shape<> > {
	typedef shape<Head,Tail> type;
};

template< typename Type, typename Head, typename Tail >
struct join< Type, shape<Head,Tail> > {
	typedef shape< Type, shape<Head,Tail> > type;
};

template< typename Type, typename Head, typename Tail >
struct join< shape<Head,Tail>, Type > {
	typedef shape< Head, typename join<Tail,Type>::type > type;
};

template< typename Type, typename Base, typename Head, typename Tail >
struct join< shape<Type,Base>, shape<Head,Tail> > {
	typedef join< Base, shape<Head,Tail> > Join;
	typedef typename join< Type, typename Join::type >::type type;
};



template< typename Type >
struct erase< Type, shape<> > {
	typedef shape<> type;
};

template< typename Type, typename Tail >
struct erase< Type, shape<Type,Tail> > {
	typedef typename erase<Type,Tail>::type type;
};


template< typename Type, typename Head, typename Tail >
struct erase< Type, shape<Head,Tail> > {
	typedef typename join< Head, typename erase<Type,Tail>::type >::type type;
};




} // end namespace night::tensor

} // end namespace night

#endif
